#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Name : __init__.py
# Author: ls_seraph@126.com
# Time  : 2021/3/6 22:46


import sys
import re
import os
import json

from frame.common.util.config_util import config as yml
from frame.common.global_data.path_param import Param


# 如需切换报告模板，只需更改继承关系
# 自定义测试执行器
class HTMLTestReport():

    def __init__(self, stream=sys.stdout, verbosity=2, title=None, description=None, tester=None):
        """
        :param stream: 新建报告文件流
        :param verbosity: 1,只输出成功与否，2会输出用例名称
        :param title: 报告标题
        :param description: 报告备注
        :param tester: 测试人
        """
        # 是否需要截图 need_screenshot 参数，-1为无需截图，否则需要截图
        self.need_screenshot = 0
        self.stream = stream
        self.verbosity = verbosity
        self.title = title
        self.description = description
        self.tester = tester

    # 生成报告
    def generateReport(self, result):
        return self.disposal_result(result)

    # 结果处理
    def disposal_result(self, result):
        head_mod = self._disposal_result_info(result)
        table_mod = self._disposal_result_data(result)
        template_path = os.path.join(Param.get_value("path"), yml.get_report_value("template"))
        file = open(template_path, 'r', encoding='UTF-8')
        html_out_arr = []
        for line in file.readlines():
            if line.strip() == 'infoData = {}':
                html_out_arr.append("infoData = " + json.dumps(head_mod, ensure_ascii=False) + "\n")
            elif line.strip() == 'tableData = []':
                html_out_arr.append("tableData = " + json.dumps(table_mod, ensure_ascii=False) + "\n")
            else:
                html_out_arr.append(line)
        return ''.join(html_out_arr)

    # 处理执行结果封装
    def _disposal_result_info(self, result):
        head_mod = {}
        sc = result.success_count  # 成功用例数
        fc = result.fail_count  # 失败用例数
        ec = result.error_count  # 错误用例数
        cc = sc + fc + ec  # 总用例数
        head_mod["fcase"] = result.fail_case if fc > 0 else "无"  # 失败用例名
        head_mod["ecase"] = result.error_case if ec > 0 else "无"  # 错误用例名
        head_mod["sc"] = sc  # 成功用例数
        head_mod["fc"] = fc  # 失败用例数
        head_mod["ec"] = ec  # 错误用例数
        head_mod["cc"] = cc  # 总用例数
        head_mod["st"] = result.all_start_time  # 开始时间
        head_mod["et"] = result.all_end_time  # 结束时间
        head_mod["ct"] = result.all_use_time  # 用时
        head_mod["title"] = self.title
        head_mod["description"] = self.description
        head_mod["tester"] = self.tester
        return head_mod

    # 处理表格数据
    def _disposal_result_data(self, result):
        rows = []
        sortedResult = self.sortResult(result.all_result)
        # 所有用例统计耗时初始化
        id_num = 0

        for cid, (cls, cls_results) in enumerate(sortedResult):
            sum_ns = 0  # 用例集耗时
            id_num += 1
            # subtotal for a class
            np = nf = ne = ns = 0
            for n, t, o, e, s in cls_results:
                if n == 0:
                    np += 1
                elif n == 1:
                    nf += 1
                elif n == 2:
                    ne += 1
                ns += s  # 把单个class用例文件里面的多个def用例每次的耗时相加
            ns = round(ns, 2)
            sum_ns += ns  # 把所有用例的每次耗时相加
            name = cls.__name__
            doc = cls.__doc__ and cls.__doc__.split("\n")[0] or ""
            row = {
                'id': id_num,
                'name': name,
                'comment': doc,
                "count": np + nf + ne,
                "success": np,
                "fail": nf,
                "error": ne,
                "time": str(round(sum_ns, 2)) + "秒"
            }
            # 用例循环
            case_arr = []
            for tid, (n, t, o, e, s) in enumerate(cls_results):
                self._get_test_case_details(case_arr, cid, tid, n, t, o, e)
            row["case"] = case_arr
            rows.append(row)
        return rows

    # 获取测试详情信息

    def _get_test_case_details(self, rows, cid, tid, n, t, o, e):
        # n 状态 0成功 1失败 2错误
        # o e 系统收集器收集的日志
        # e.g. 'pt1_1', 'ft1_1', 'et1_1'etc
        has_output = bool(o or e)
        # ID修改点为下划线,支持Bootstrap折叠展开特效 - Findyou

        name = t.id().split('.')[-1]
        doc = t.shortDescription() or ""

        # o and e should be byte string because they are collected from stdout and stderr?
        # o e 系统收集器收集的日志
        if isinstance(o, str):
            # TODO: some problem with 'string_escape': it escape \n and mess up formating
            # uo = unicode(o.encode('string_escape'))
            # uo = o.decode('latin-1')
            uo = o
        else:
            uo = o
        if isinstance(e, str):
            # TODO: some problem with 'string_escape': it escape \n and mess up formating
            # ue = unicode(e.encode('string_escape'))
            # ue = e.decode('latin-1')
            ue = e
        else:
            ue = e

        # 截图名字通过抛出异常存放在u，通过截取字段获得截图名字  -- Gelomen
        # u 为stdout 和 stderr 收集器合集
        u = uo + ue
        # 先判断是否需要截图
        self.need_screenshot = u.find("errorImg[")
        row = {}
        """
        'name': 'test_login_fail_1',
        'comment': '对应测试报告中的说明文件验证登陆失败',
        "result": 123,
        "details": "内容",
        "drive": "chrome(87.0.4280.141)",
        "screenshot": "image/5.png"
        """
        if self.need_screenshot == -1:  # 无截图

            # row = tmpl % dict(
            #     tid=tid,
            #     Class=(n == 0 and 'hiddenRow' or 'none'),
            #     style=n == 2 and 'errorCase' or (n == 1 and 'failCase' or 'passCase'),
            #     name=name,
            #     doc=doc,
            #     script=script,
            #     status=self.STATUS[n],
            # )
            row = {
                "name": name,
                "comment": doc,
                "result": n,
                "details": u,
                "drive": "",
                "screenshot": ""
            }
        else:
            screenshot_list = re.findall("errorImg\[(.*?)\]errorImg", u)
            screenshot_list = ["image/" + i for i in screenshot_list]
            # 取两个字符之间的内容
            browser = u[u.find('browser[') + 8:u.find(']browser')]

            row = {
                "name": name,
                "comment": doc,
                "result": n,
                "details": u,
                "drive": browser,
                "screenshot": screenshot_list
            }
        rows.append(row)

        if not has_output:
            return

    def sortResult(self, result_list):
        # unittest does not seems to run in any particular order.
        # Here at least we want to group them together by class.
        rmap = {}
        classes = []
        for n, t, o, e, s in result_list:
            cls = t.__class__
            if cls not in rmap:
                rmap[cls] = []
                classes.append(cls)
            rmap[cls].append((n, t, o, e, s))
        r = [(cls, rmap[cls]) for cls in classes]
        return r

